package handler

import (
	"bbs-web/internal/domain"
	"bbs-web/internal/service"
	article3 "bbs-web/internal/service/article"
	"bbs-web/internal/web/jwtx"
	"bbs-web/internal/web/resp"
	"bbs-web/internal/web/vo"
	"bbs-web/pkg/ginplus"
	"bbs-web/pkg/logger"
	"bbs-web/pkg/utils/zifo/slice"
	"fmt"
	"github.com/gin-gonic/gin"
	"golang.org/x/sync/errgroup"
	"strconv"
)

type ArticleHandler struct {
	svc   article3.IArticleService
	log   logger.Logger
	biz   string
	inter service.InteractiveService
}

func NewArticleHandler(svc article3.IArticleService, log logger.Logger, inter service.InteractiveService) *ArticleHandler {
	return &ArticleHandler{svc: svc, log: log, biz: "article", inter: inter}
}

func (h *ArticleHandler) CollectEntityByID(ctx *gin.Context, user jwtx.UserClaims) (ginplus.Result, error) {
	collectid := ctx.Param("cid")
	cid, err := strconv.ParseInt(collectid, 10, 64)
	if err != nil {
		return ginplus.Result{
			Code: 0,
			Msg:  "参数错误",
			Data: nil,
		}, err
	}

	bids := ctx.Param("bid")
	bid, err := strconv.ParseInt(bids, 10, 64)
	if err != nil {
		return ginplus.Result{
			Code: 0,
			Msg:  "参数错误",
			Data: nil,
		}, err
	}

	err2 := h.inter.CollectEntity(ctx, h.biz, user.Id, cid, bid)
	if err2 != nil {
		return ginplus.Result{
			Msg: "系统错误",
		}, err2
	}
	return ginplus.Result{
		Msg: "OK",
	}, nil
}

func (h *ArticleHandler) Collected(ctx *gin.Context, req vo.CollectReq, user jwtx.UserClaims) (ginplus.Result, error) {
	var err error
	if req.Collect {
		err = h.inter.Collected(ctx, h.biz, req.Id, user.Id)
		if err != nil {
			return ginplus.Result{}, err
		}

	} else {
		err = h.inter.DisCollected(ctx, h.biz, req.Id, user.Id)
		if err != nil {
			return ginplus.Result{}, err
		}
	}
	return ginplus.Result{
		Msg: "OK",
	}, nil
}

func (h *ArticleHandler) Liked(ctx *gin.Context, req vo.LikeReq, user jwtx.UserClaims) (ginplus.Result, error) {
	var err error
	if req.Like {
		err = h.inter.Liked(ctx, h.biz, req.Id, user.Id)
		if err != nil {
			return ginplus.Result{}, err
		}
	} else {
		err = h.inter.DisLiked(ctx, h.biz, req.Id, user.Id)
		if err != nil {
			return ginplus.Result{}, err
		}
	}
	return ginplus.Result{
		Msg: "ok",
	}, nil
}

func (h *ArticleHandler) PubDetail(ctx *gin.Context, user jwtx.UserClaims) (ginplus.Result, error) {
	articleid := ctx.Param("id")
	artid, err := strconv.ParseInt(articleid, 10, 64)
	if err != nil {
		return ginplus.Result{
			Code: 0,
			Msg:  "参数错误",
			Data: nil,
		}, err
	}

	//使用errgroup，只要协程报错，其他协程终止，需要一致的错误处理和资源清理时。返回错误
	var (
		eg      errgroup.Group
		article domain.Article
		intr    domain.Interactive
	)

	//可以异步获取文章详情
	eg.Go(func() error {
		var err error
		article, err = h.svc.GetPubById(ctx, artid, user.Id)
		return err
	})
	//可以文章的品论，点赞，收藏等信息
	eg.Go(func() error {
		var err error
		intr, err = h.inter.Get(ctx, h.biz, artid, user.Id)
		return err
	})

	er := eg.Wait()
	if er != nil {
		return ginplus.Result{
			Code: 502005,
			Msg:  "系统错误",
		}, er
	}

	return ginplus.Result{
		Data: resp.ArticleResp{
			Id:          article.Id,
			Title:       article.Title,
			AuthorId:    article.Author.Id,
			AuthorName:  article.Author.UserName,
			Status:      article.Status.String(),
			Summary:     article.Summary,
			Content:     article.Content,
			LikeCnt:     intr.LikeCnt,
			ReadCnt:     intr.ReadCnt,
			CommentCnt:  intr.CommentCnt,
			CollectCnt:  intr.CollectCnt,
			ContentType: article.ContentType,
			Cover:       article.Cover,
			Ctime:       article.Ctime,
			Utime:       article.Utime,
		},
	}, nil

}

func (h *ArticleHandler) CreateCollect(ctx *gin.Context, req vo.CreateCollectReq, user jwtx.UserClaims) (ginplus.Result, error) {
	collect, err := h.inter.CreateCollect(ctx, user.Id, req.CollectName, req.Desc, req.IsPublic)
	if err != nil {
		return ginplus.Result{
			Code: 5003402,
			Msg:  "创建失败",
		}, err
	}
	return ginplus.Result{
		Data: collect,
		Msg:  "OK",
	}, nil
}

func (h *ArticleHandler) GetCollectById(ctx *gin.Context) (ginplus.Result, error) {
	userid := ctx.Param("id")
	uid, err := strconv.ParseInt(userid, 10, 64)
	if err != nil {
		return ginplus.Result{
			Code: 0,
			Msg:  "参数错误",
			Data: nil,
		}, err
	}

	data, err := h.inter.GetByUid(ctx, uid, 100, 1)
	if err != nil {
		return ginplus.Result{}, err
	}
	return ginplus.Result{
		Data: data,
	}, nil
}

func (h *ArticleHandler) PubAuthorArtList(ctx *gin.Context) (ginplus.Result, error) {
	authorid := ctx.Param("id")
	auid, err := strconv.ParseInt(authorid, 10, 64)
	if err != nil {
		return ginplus.Result{
			Code: 0,
			Msg:  "参数错误",
			Data: nil,
		}, err
	}

	//可以异步获取文章列表，不需要顺序执行，阻塞等待
	//使用errgroup，只要协程报错，其他协程终止，需要一致的错误处理和资源清理时。返回错误
	var (
		eg   errgroup.Group
		arts []domain.Article
	)

	eg.Go(func() error {
		var err error
		arts, err = h.svc.List(ctx, auid, 1, 15)
		return err
	})

	er := eg.Wait()
	if er != nil {
		return ginplus.Result{
			Msg:  "系统错误",
			Data: nil,
		}, err
	}
	return ginplus.Result{
		//列表页全部展示
		//Data: arts,
		//列表页不展示context
		Data: slice.Map[domain.Article, resp.ArticleResp](arts, func(idx int, src domain.Article) resp.ArticleResp {
			//列表页不展示context
			return resp.ArticleResp{
				Id:          src.Id,
				Title:       src.Title,
				AuthorId:    src.Author.Id,
				AuthorName:  src.Author.UserName,
				Status:      src.Status.String(),
				Summary:     src.Summary,
				ContentType: src.ContentType,
				Cover:       src.Cover,
				Ctime:       src.Ctime,
				Utime:       src.Utime,
			}
		}),
	}, nil
}

func (h *ArticleHandler) List(ctx *gin.Context, req vo.ArticleListReq, user jwtx.UserClaims) (ginplus.Result, error) {
	list, err := h.svc.List(ctx, user.Id, req.PageNum, req.PageSize)
	if err != nil {
		return ginplus.Result{
			Code: 502001,
			Msg:  err.Error(),
		}, err
	}
	return ginplus.Result{
		Data: slice.Map[domain.Article, resp.ArticleResp](list, func(idx int, src domain.Article) resp.ArticleResp {
			//列表页不展示context
			return resp.ArticleResp{
				Id:          src.Id,
				Title:       src.Title,
				AuthorId:    src.Author.Id,
				AuthorName:  src.Author.UserName,
				Status:      src.Status.String(),
				Summary:     src.Summary,
				ContentType: src.ContentType,
				Cover:       src.Cover,
				Ctime:       src.Ctime,
				Utime:       src.Utime,
			}
		}),
	}, nil
}

func (h *ArticleHandler) Edit(ctx *gin.Context, req vo.ArticleReq, user jwtx.UserClaims) (ginplus.Result, error) {
	//编辑文章还是新建文章，都是用户前端输入内容的，其他不变，最后只需要返回文章的id。在编辑存在的update，通过选择前端对应的表格的文章id，如果是新建的就是前端处理id=0，返回的id方便前端设置
	id, err := h.svc.Save(ctx.Request.Context(), req.ToDomain(user.Id))
	if err != nil {
		h.log.Error("编辑文章出错", logger.Error(err))
		return ginplus.Result{
			Code: 510002,
			Msg:  "编辑文章出错",
		}, err
	}
	return ginplus.Result{
		Data: id,
	}, nil
}

func (h *ArticleHandler) Publish(ctx *gin.Context, req vo.ArticleReq, user jwtx.UserClaims) (ginplus.Result, error) {
	//跟edit方法类似，只不过这是编辑+发布，就是发布库和制作库两个同步更新，也是有编辑和新建的操作
	id, err := h.svc.Publish(ctx, req.ToDomain(user.Id))
	if err != nil {
		h.log.Error("发布文章失败", logger.Error(err))
		return ginplus.Result{
			Code: 510003,
			Msg:  "发布文章失败",
			Data: nil,
		}, err
	}
	return ginplus.Result{
		Data: id,
	}, nil
}

func (h *ArticleHandler) Withdraw(ctx *gin.Context, user jwtx.UserClaims) (ginplus.Result, error) {
	//在 Gin 中，可以使用 gin.Context 对象的方法来获取 URL 中的参数。文章id在前端 axios 或者 fetch库选择对应的下架按钮会自动构建一个url去请求后端的这个接口，axios.post(`/articles/${articleId}/withdraw`, {
	articleid := ctx.Param("id")
	id, err := strconv.ParseInt(articleid, 10, 64)
	if err != nil {
		return ginplus.Result{
			Msg:  "参数错误",
			Data: nil,
		}, err
	}

	err = h.svc.Withdraw(ctx, domain.Article{
		Id: id,
		Author: domain.Author{
			Id: user.Id,
		},
	})

	if err != nil {
		return ginplus.Result{
			Msg: "文章贴子下架失败",
		}, err
	}
	return ginplus.Result{}, nil
}

func (h *ArticleHandler) Detail(ctx *gin.Context, user jwtx.UserClaims) (ginplus.Result, error) {
	//在 Gin 中，可以使用 gin.Context 对象的方法来获取 URL 中的参数。文章id在前端 axios 或者 fetch库选择对应的下架按钮会自动构建一个url去请求后端的这个接口，axios.post(`/articles/${articleId}/withdraw`, {
	articleid := ctx.Param("id")
	id, err := strconv.ParseInt(articleid, 10, 64)
	if err != nil {
		return ginplus.Result{
			Msg:  "参数错误",
			Data: nil,
		}, err
	}

	art, err := h.svc.GetById(ctx, id)
	if err != nil {
		return ginplus.Result{
			Code: 502001,
			Msg:  err.Error(),
		}, err
	}
	// 说明非法访问 ，需要做反爬,或者上报风控系统
	//反爬策略
	if art.Author.Id != user.Id {
		return ginplus.Result{
			Msg: "输入错误",
		}, fmt.Errorf("非法访问文章，创作者 ID 不匹配 %d", user.Id)
	}

	return ginplus.Result{
		Data: resp.ArticleResp{
			Id:          art.Id,
			Title:       art.Title,
			AuthorId:    art.Author.Id,
			AuthorName:  art.Author.UserName,
			Status:      art.Status.String(),
			Summary:     art.Summary,
			Content:     art.Content,
			ContentType: art.ContentType,
			Cover:       art.Cover,
			Ctime:       art.Ctime,
			Utime:       art.Utime,
		},
	}, nil
}
